﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SimpleRayTracer
{
    // From http://www.codeproject.com/KB/graphics/Simple_Ray_Tracing_in_C_.aspx

    class Sphere : Object3D
    {
        private double radius;
        private Vector3D centre;

        public Sphere(Vector3D position, double r, double colourRed, double colourGreen, double colourBlue)
        {
            centre = position;
            radius = r;
            clR = colourRed;
            clG = colourGreen;
            clB = colourBlue;
        }

        public override bool IsIntersect(Ray ray)
        {
            return true;
        }

        public override bool GetIntersect(Ray ray, ref double distance)
        {
            Vector3D source = ray.origin;
            Vector3D direction = ray.direction;

            double px3 = centre.x;
            double py3 = centre.y;
            double pz3 = centre.z;
            double px1 = source.x;
            double py1 = source.y;
            double pz1 = source.z;
            double px2 = direction.x;
            double py2 = direction.y;
            double pz2 = direction.z;

            double A = Sqr(px2 - px1) + Sqr(py2 - py1) + Sqr(pz2 - pz1);

            double B = 2 * ((px2 - px1) * (px1 - px3) + (py2 - py1) * (py1 - py3) + (pz2 - pz1) * (pz1 - pz3));

            double C = Sqr(px3) + Sqr(py3) + Sqr(pz3) + Sqr(px1) + Sqr(py1) + Sqr(pz1) -
                2 * ((px3 * px1) + (py3 * py1) + (pz3 * pz1)) - Sqr(radius);


            //double A = Vector3D.Dot(direction, direction);

            //double B = Vector3D.Dot(Vector3D.ScalarMult(Vector3D.Subtract(source, centre), 2), direction);

            //double C = Vector3D.Dot(Vector3D.Subtract(source, centre), Vector3D.Subtract(source, centre)) - Sqr(radius);

            double D = Sqr(B) - 4 * A * C;

            double t = -1.0;

            if (D >= 0)
            {
                double t1 = (-B - System.Math.Sqrt(D)) / (2.0 * A);
                double t2 = (-B + System.Math.Sqrt(D)) / (2.0 * A);
                if (t1 > t2) t = t1; else t = t2;

                distance = t;
                return true;
            }

            distance = t;
            return false;
        }

        public override Vector3D GetNormal(Vector3D v)
        {
            Vector3D result = new Vector3D();

            result = (v - centre) * radius;

            result = Vector3D.Normv(result);

            return result;
        }

        public override void Move(Vector3D trans, Vector3D centre)
        {
            centre.x += trans.x;
            centre.y += trans.y;
            centre.z += trans.z;
        }

        public override void MoveTo(Vector3D dest, Vector3D centre)
        {
            centre.x = dest.x;
            centre.y = dest.y;
            centre.z = dest.z;
        }

        public override void RotX(double angle, Vector3D centre)
        {
            double y = centre.y * System.Math.Cos(angle) - centre.z * System.Math.Sin(angle);
            double z = centre.y * System.Math.Sin(angle) + centre.z * System.Math.Cos(angle);
            centre.y = y;
            centre.z = z;
        }

        public override void RotY(double angle, Vector3D centre)
        {
            double x = centre.x * System.Math.Cos(angle) - centre.z * System.Math.Sin(angle);
            double z = centre.x * System.Math.Sin(angle) + centre.z * System.Math.Cos(angle);
            centre.x = x;
            centre.z = z;
        }

        public override void RotZ(double angle, Vector3D centre)
        {
            double x = centre.x * System.Math.Cos(angle) - centre.y * System.Math.Sin(angle);
            double y = centre.x * System.Math.Sin(angle) + centre.y * System.Math.Cos(angle);
            centre.x = x;
            centre.y = y;
        }
    }
}
